package metaRobot;

import java.awt.geom.Point2D;

import robocode.*;

/**
 * Classe contenente i metodi personalizzati per il robot, in modo da compilarli una sola volta.
 */
public class ACRobot extends AdvancedRobot
{	
	public static double getDistance(Point2D.Double point1, Point2D.Double point2)
	{
		return Math.sqrt(Math.pow((point1.getX() - point2.getX()), 2) + Math.pow((point1.getY() - point2.getY()), 2));
	}
	
    public static Point2D.Double project(Point2D.Double sourceLocation, double angle, double length) 
    {
    	return new Point2D.Double(sourceLocation.x + Math.sin(angle) * length, sourceLocation.y + Math.cos(angle) * length);
    }
    
    public static double absoluteBearing(Point2D.Double source, Point2D.Double target) 
    {
        return Math.atan2(target.x - source.x, target.y - source.y);
    }
    
	public void turnRadarToGun()
	{
		double newAngle = getGunHeadingRadians() - getRadarHeadingRadians();

		if (Math.abs(newAngle) < Math.PI)
			setTurnRadarRightRadians(newAngle);
		else
		{
			if (newAngle > 0)
				setTurnRadarRightRadians(newAngle - 2*Math.PI);
			else
				setTurnRadarRightRadians(newAngle + 2*Math.PI);
		}
	}

	public void turnRadarToEnemy(Point2D.Double enemyLocation)
	{
		if (enemyLocation == null)
			return;

		double angle = getRadarHeadingRadians() - absoluteBearing(new Point2D.Double(getX(), getY()), enemyLocation);
		
		if (Math.abs(angle) > Math.PI)
		{
			if (angle > 0)
				angle = angle - 2*Math.PI;
			else
				angle = angle + 2*Math.PI;
		}
		
		setTurnRadarLeftRadians(angle);
	}
	
	public boolean testEnemyWithinTicksOfFire(ScannedRobotEvent e, int distance, int firePower)
	{
		return e.getDistance() < distance*(20 - 3 * firePower);
	}
	
	public boolean testEnemyWithinTicks(Point2D.Double enemyLocation, int ticks)
	{
		if (enemyLocation == null)
			return false;
		
		return getDistance(enemyLocation, new Point2D.Double(getX(), getY())) - getVelocity()*ticks < 0;
	}	
	
	public boolean isCornered()
	{
		Point2D.Double location = new Point2D.Double(getX(), getY());
		if (getDistance(location, new Point2D.Double(0, 0)) < 100)
			return true;
		else if (getDistance(location, new Point2D.Double(0, getBattleFieldHeight())) < 100)
			return true;
		else if (getDistance(location, new Point2D.Double(getBattleFieldWidth(), 0)) < 100)
			return true;
		else if (getDistance(location, new Point2D.Double(getBattleFieldWidth(), getBattleFieldHeight())) < 100)
			return true;
		
		return false;
	}
	
	public void turnGunToEnemy(ScannedRobotEvent e)
	{
		setTurnGunRight(e.getBearing());
	}
	
	public void turnGunToEnemy(Point2D.Double enemyLocation)
	{
		if (enemyLocation == null)
				return;
	
		setTurnGunLeftRadians(getGunHeadingRadians() - absoluteBearing(new Point2D.Double(getX(), getY()), enemyLocation));
	}
	public void moveToCenter(){
		getHeading();
		int xMiddle= (int) (getBattleFieldWidth()/2);
		int yMiddle = (int) (getBattleFieldHeight()/2);
		double bearingToCentre = getBearing(xMiddle, yMiddle);
		setTurnRightRadians(bearingToCentre);
		setAhead(Point2D.distance(getX(), getY(), xMiddle, yMiddle));
	}
	double norm(double a) {
		  // do something smarter with modulus (%) here
		  while (a <= -Math.PI) a += 2 * Math.PI;
		  while (Math.PI < a) a -= 2 * Math.PI;
		  return a;
		}
	double getBearing(int x, int y) {
		  // can rotate the coordinate system to avoid the addition of pi/2 if you like 
		  double b = Math.PI/2 - Math.atan2(y - getY(), x - getX());
		  return norm(b - getHeadingRadians());
		}
	
	public void moveToLowLeft(){
		getHeading();
		int xLowLeft= 40;
		int yLowLeft = 40;
		double bearingToLowLeft = getBearing(xLowLeft, yLowLeft);
		setTurnRightRadians(bearingToLowLeft);
		setAhead(Point2D.distance(getX(), getY(), xLowLeft, yLowLeft));
	}
	public void moveToLowRight(){
		getHeading();
		int xLowRight= (int) (getBattleFieldWidth()-40);
		int yLowRight = 40;
		double bearingToLowLeft = getBearing(xLowRight, yLowRight);
		setTurnRightRadians(bearingToLowLeft);
		setAhead(Point2D.distance(getX(), getY(), xLowRight, yLowRight));
	}
	public void moveToUpLeft(){
		getHeading();
		int xUpLeft = 40;
		int yUpLeft = (int) (getBattleFieldHeight()-40);
		double bearingToLowLeft = getBearing(xUpLeft, yUpLeft);
		setTurnRightRadians(bearingToLowLeft);
		setAhead(Point2D.distance(getX(), getY(), xUpLeft, yUpLeft));
	}
	public void moveToUpRight(){
		getHeading();
		int xUpRight = (int) (getBattleFieldWidth()-40);
		int yUpRight = (int) (getBattleFieldHeight()-40);
		double bearingToLowLeft = getBearing(xUpRight, yUpRight);
		setTurnRightRadians(bearingToLowLeft);
		setAhead(Point2D.distance(getX(), getY(), xUpRight, yUpRight));
	}
	public void moveToEnemy(Point2D.Double enemyLocation){
		if (enemyLocation == null)
			return;
		getHeading();
		double bearingToEnemy = getBearing((int)enemyLocation.x, (int)enemyLocation.y);
		setTurnRightRadians(bearingToEnemy);
		setAhead(Point2D.distance(getX(), getY(), enemyLocation.x, enemyLocation.y));
	}
	public void moveAwayFromEnemy(Point2D.Double enemyLocation){
		if (enemyLocation == null)
			return;
		getHeading();
		double bearingToEnemy = getBearing((int)enemyLocation.x, (int)enemyLocation.y);
		setTurnRightRadians(bearingToEnemy);
		setBack(Point2D.distance(getX(), getY(), enemyLocation.x, enemyLocation.y));
	}
	public void turnParallelToNearestWall(){
		getHeading();
		double bearing;
		if (getX()<getBattleFieldWidth()/2 && getY()<getBattleFieldHeight()/2){
			if (getX()<getY()){
				bearing = getBearing((int)getX(), 0);
			}else{
				bearing = getBearing((int)getBattleFieldWidth(), (int)getY());
			}
		}else{
			if (getX()<getBattleFieldWidth()/2 && getY()>getBattleFieldHeight()/2){
				if (getX()<getBattleFieldHeight()-getY()){
					bearing = getBearing((int)getX(), 0);
				}else{
					bearing = getBearing((int)getBattleFieldWidth(), (int)getY());
				}
			}else{
				if (getX()>getBattleFieldWidth()/2 && getY()>getBattleFieldHeight()/2){
					if (getBattleFieldWidth()-getX()<getBattleFieldHeight()-getY()){
						bearing = getBearing((int)getX(), 0);
					}else{
						bearing = getBearing(0, (int)getY());
					}
				}else{
					if(getBattleFieldWidth()-getX()<getY()){
						bearing = getBearing((int)getX(), (int)getBattleFieldHeight());
					}else{
						bearing = getBearing(0, (int)getY());
					}
				}	
			}
		}
	  setTurnRightRadians(bearing);
	}
	public void turnPerpendicularToNearestWall(){
		getHeading();
		double bearing;
		if (getX()<getBattleFieldWidth()/2 && getY()<getBattleFieldHeight()/2){
			if (getX()<getY()){
				bearing = getBearing((int)getBattleFieldWidth(),(int)getY());
			}else{
				bearing = getBearing((int)getX(), (int)getBattleFieldHeight());
			}
		}else{
			if (getX()<getBattleFieldWidth()/2 && getY()>getBattleFieldHeight()/2){
				if (getX()<getBattleFieldHeight()-getY()){
					bearing = getBearing((int)getBattleFieldWidth(), (int)getY());
				}else{
					bearing = getBearing((int)getX(), 0);
				}
			}else{
				if (getX()>getBattleFieldWidth()/2 && getY()>getBattleFieldHeight()/2){
					if (getBattleFieldWidth()-getX()<getBattleFieldHeight()-getY()){
						bearing = getBearing(0, (int)getY());
					}else{
						bearing = getBearing((int)getX(), 0);
					}
				}else{
					if(getBattleFieldWidth()-getX()<getY()){
						bearing = getBearing(0, (int)getY());
					}else{
						bearing = getBearing((int)getX(), (int)getBattleFieldHeight());
					}
				}	
			}
		}
	  setTurnRightRadians(bearing);
	}
	public void move(boolean ahead, int distance, boolean left, int degrees){
		if (ahead)
			setAhead(distance);
		else
			setBack(distance);
		if (left)
			setTurnLeft(degrees);
		else
			setTurnRight(degrees);
	}
	public void noOperation(){
		return;
	}
}
